查看原文
其他

讲讲怎么枚举MmMapViewInSystemSpace分配的内存

hzqst 看雪学院 2019-05-25


之前曝光了一个利用

MmMapViewInSystemSpace往驱动空间所在区域分配内存的外挂传送门:

https://bbs.pediy.com/thread-230129.htm

闲的蛋疼就研究了一下怎么枚举这种方法分配出来的内存。


首先明确一下虚拟内存的分配流程:

vista以上:

MmMapViewInSystemSpace ->MiMapViewInSystemSpace->MiInsertInSystemSpace->MiReserveSystemPtes(&MiSystemPteInfo, PteCount)

(不同系统可能不同,但是总之是预留PTE)

xp:

MmMapViewInSystemSpace ->MiMapViewInSystemSpace->MiInsertInSystemSpace->

从SystemSpaceBitMap找一块满足sizein64k大小的空的bitmap出来,对应到预先分配好的SystemSpaceViewStart里,直接作为分配出的地址。


物理内存的分配流程:

刚才分配的PTE present位为0,访问时触发pagefault再分配物理内存,原理和NtMapViewOfSection差不多,都是只管分配VA,访问时#pf才给分配物理内存。

这种情况下分配物理内存时不会往MmPfnDatabase中写入pfn对应的pte信息(毕竟一个section可以被多个PTE映射不是么),导致MmGetVirtualForPhysical无法从物理地址反查虚拟地址。


我们可以找个有符号的win7看一下:

发现和wrk差不多,直接看wrk。


关键代码:

Entry = (ULONG_PTR) MI_64K_ALIGN(Base) + SizeIn64k;

   Hash = (ULONG) ((Entry >> 16) % Session->SystemSpaceHashKey);

   while (Session->SystemSpaceViewTable[Hash].Entry != 0) {
       Hash += 1;
       if (Hash >= Session->SystemSpaceHashSize) {
           Hash = 0;
       }
   }

   Session->SystemSpaceHashEntries += 1;

   Session->SystemSpaceViewTable[Hash].Entry = Entry;
   Session->SystemSpaceViewTable[Hash].ControlArea = ControlArea;

那么只要遍历MmSession的SystemSpaceViewTable就能找到所有的va。


entry的算法是这样的:

entry=64k对齐(虚拟地址)  +  多少个64k

也就是说entry的高48位是虚拟地址,低16位是大小(指有几个64k)


从分配前的检查也可以看出,不能分配超过(65535=64k-1)个64k,否则超过低16位能保存的大小了。

算出来的entry会简单计算一个hash放到 SystemSpaceViewTable 里。

那么我们遍历这个表就能拿到所有的SystemSpaceView VA和大小了,遍历前记得加锁SystemSpaceViewLockPointer。

PMMSESSION_WIN7 Session = (PMMSESSION_WIN7)MmSession;
for (ULONG i = 0; i < Session->SystemSpaceHashSize; i ++) {
   if (Session->SystemSpaceViewTable[i].Entry != 0) {
           PVOID BaseAddress = (PVOID)(((Session->SystemSpaceViewTable[i].Entry >> 16) << 16) | 0xFFFF000000000000ull);//Higher 48 bit of Entry is BaseAddress aligned to 64k
           SIZE_T ViewSize = (Session->SystemSpaceViewTable[i].Entry & 0xFFFFull) * 0x10000;//lowest 16 bit of Entry is SizeIn64k
           //Now you get BaseAddress and ViewSize, ViewSize is count in bytes.
       }
   }
}


32位系统的区别仅在于高16位是虚拟地址,低16位是大小,其他和64位没有任何区别 ,因此以上代码甚至只需要把PMMESSION结构换成32位版本就行了。

至于PMMSESSION结构,自己找个有符号的win7自提。

MMSESSION MmSession变量在MmMapViewInSystemSpace入口一个lea rdx就有。

以上遍历兼容xp~win8.1。


遍历结果:


然后看一下win10的 MiInsertInSystemSpace ,貌似变化有点大,不用hash表而是用红黑树了。

typedef struct _MMSESSION_WIN10
{
   EX_PUSH_LOCK SystemSpaceViewLock;
   PEX_PUSH_LOCK SystemSpaceViewLockPointer;
   PRTL_AVL_TREE ViewRoot;
   ULONG ViewCount;
   ULONG BitmapFailures;
}MMSESSION_WIN10, *PMMSESSION_WIN10;


ViewRoot这个 RTL_BALANCED_NODE 其实是一个侵入式数据结构,他的大小远不止sizeof( RTL_AVL_NODE )这么一点,而是:

而是有0x60这么大。


遍历这个红黑树需要你有 RTL_BALANCED_NODE 所在完整的结构,我随便逆了一下,只拿了几个重要的成员:

typedef struct __declspec(align(8)) _MMVIEW_WIN10
{
   RTL_BALANCED_NODE SectionNode;
   ULONG64 Unkown1;
   ULONG_PTR ViewSize;
   ULONG_PTR Unkown2;
   PVOID ControlArea;
   PVOID FileObject;
   ULONG_PTR Unknown3;
   ULONG_PTR Unknown4;
   PVOID SessionViewVa;
   ULONG Unknown5;
   ULONG Unknown6;
}MMVIEW_WIN10, *PMMVIEW_WIN10;


那我们遍历红黑树岂不是写个递归就完事了,要注意写入VA的时候会遇上一些奇怪的bit,我们要去掉:

void EnumSystemSpaceViewWin10(PMMVIEW_WIN10 view)
{
   PVOID BaseAddress = (PVOID)((ULONG_PTR)view->SessionViewVa & (~3));

   //now you have
   //BaseAddress
   //view->ViewSize

   PMMVIEW_WIN10 right = (PMMVIEW_WIN10)view->SectionNode.Right;

   if (right)
   {
       EnumSystemSpaceViewWin10(right);
   }

   PMMVIEW_WIN10 left = (PMMVIEW_WIN10)view->SectionNode.Left;

   if (left)
   {
       EnumSystemSpaceViewWin10(left);
   }
}


EnumSystemSpaceViewWin10((PMMVIEW_WIN10)Session->ViewRoot);


记得win10也要加锁,而且锁换成了PUSH_LOCK。

然后你拿这份代码试了一下,发现在systemspaceview非常多的情况下会栈溢出爆炸。因为递归层数太多了,你需要把递归算法改成循环。

修改过的循环遍历代码由于过于丑陋这里就不放了,类似于这种(以下代码是网上抄的)

void xunhuanzhongxubianli(BinaryTreeNode * root)
{
   stack<BinaryTreeNode*> ss;
   if (root == NULL)
   {
       return;
   }

   BinaryTreeNode * pRoot = root;

   while (pRoot || ss.size() > 0)
   {
       while (pRoot)
       {
           ss.push(pRoot);
           pRoot = pRoot->m_pLeft;
       }
       BinaryTreeNode * temp = ss.top();
       ss.pop();
       cout << temp->m_nValue << " ";
       pRoot = temp->m_pRight;
   }
}


win10下遍历结果确实有点多,递归很容易爆栈:



- End -




看雪ID:hzqst         

https://bbs.pediy.com/user-619065.htm




本文由看雪论坛 hzqst 原创

转载请注明来自看雪社区






热门技术文章推荐:





戳原文,看看大家都是怎么说的?

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存